package org.oimwrapper.connectors.genericldap;import java.util.HashSet;import java.util.Iterator;import java.util.Map;import java.util.Set;import java.util.Stack;import java.util.TreeSet;import org.oimwrapper.connectors.OIMAdapterBase;import org.oimwrapper.connectors.OIMExceptionHandler;import com.thortech.xl.dataaccess.tcDataProvider;/** * Contains methods for creating, updating and removing user accounts and associated objects  * within the an LDAP directory. */public class OIMLdapUserOperations extends OIMAdapterBase {	/**	 * This is a reference to the ldap connection object used by this instance.	 */	protected OIMLdapServer ldap;		/**	 * This contains the rootDN for this instance.	 */	protected String rootDN;		/**	 * This contains the name of the ldap attribute that holds a user's first name.	 */	protected String firstNameAttribute;		/**	 * This contains the name of the ldap attribute that holds a user's last name.	 */	protected String lastNameAttribute;		/**	 * This contains the name of the ldap attribute that holds a user's login.	 */	protected String loginAttribute;		/**	 * This contains the name of the ldap attribute that holds a user's password.	 */	protected String passwordAttribute;		/**	 * This contains the name of the ldap attribute that holds a the object class for an object.	 */	protected String objectClassAttribute;		/**	 * This contains the name of the object class that is used to find user objects.	 */	protected String defaultUserObjectClass;		/**	 * This contains a list of all objectClasses that should be used when creating a new user.	 */	protected String[] defaultUserObjectClasses;		/**	 * This should be <code>true</code> if the group object contains a DN refering to the member, or	 * <code>false</code> if the group object only contains the username of the member.	 */	protected boolean useDnForMember;		/**	 * This is the name of the ldap attribute where the group membership is stored.	 */	protected String groupMemberAttribute;		/**	 * This contains the name of the ldap attribute that should be used to form the RDN for a user.	 */	protected String primaryUserAttribute;		/**	 * This contains the name of the ldap attribute used to locate a user.	 */	protected String userSearchAttribute;		/**	 * This contains the name of the ldap attribute that stores the container name of an object.	 */	protected String cnAttribute;		/**	 * This contains the name of the ldap attribute that stores the distingushed name of an object.	 */	protected String dnAttribute;		/**	 * This contains the name of the object class for an organizational unit.	 */	protected String ouObjectClass;		/**	 * This contains name of the primary ldap attribute used to form the RDN of an organizational unit.	 */	protected String ouAttribute;		/**	 * This contains the name of the ldap attribute used to locate a group.	 */	protected String groupSearchAttribute;		/**	 * This contains the primary ldap attribute used to form the RDN of a group object.	 */	protected String groupIDAttribute;		/**	 * This contains the name of the object class that should be used when locating a group object.	 */	protected String defaultGroupObjectClass;		/**	 * This contains the base DN to create a new user under when the DN is not specified.	 */	protected String userCreationBaseDN;		/**	 * This is <code>true</code> if this instance is properly initialized, or <code>false</code> if this object is not.	 */	protected boolean isInitialized;		/**	 * The parsing utilities used by this isntance.	 */	protected OIMLdapParsingUtils parsingUtil;	/**	 * This constructor is the best to use in process task adapters.	 *	 * @param ProcessInstanceKey The key value pointing to the current process.  This should be setup as variable of long type in 	 *                           the adapter, and mapped to the process instance key in the process definition as found under 	 *                           <code>Process Data -&gt; Process Instance</code>.	 * @param ProcessFormField   The name of the process form field containing the Generic LDAP Server IT Resource that should	 *                           should be used for the operations.   This should be a literal string of the form 	 *                           <code>UD_MYFORM_SERVER</code>.	 * @param Database           This is a reference to the current database connector, as found under <code>Adapter References -&gt; 	 *                           Database Reference</code> in the Adapter Factory.	 */	public OIMLdapUserOperations(long ProcessInstanceKey, String ProcessFormField, tcDataProvider Database) {		super("GENERICLDAP.PROV", Database);		isInitialized = false;		logger.debug("Entering ::ldapUserOperations()");		logger.debug("ProcessInstanceKey = " + Long.toString(ProcessInstanceKey));		logger.debug("ProcessFormField = " + ProcessFormField);		try {			long itResourceKey = Long.parseLong(getProcessFormValue(ProcessInstanceKey, ProcessFormField));			init(itResourceKey);		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		}		logger.debug("Exiting ::ldapUserOperations()");	}	/**	 * This constructor is the best to use in entity adapters	 *	 * @param ITResourceInstanceKey This key value points to the IT resource instance.   This should be setup as variable of long type 	 *                              in the adapter, and mapped to the IT resource column on the form in the Data Object Manager.	 * @param Database              This is a reference to the current database connector, as found under <code>Adapter References -&gt; 	 *                              Database Reference</code> in the Adapter Factory.	 */	public OIMLdapUserOperations(long ITResourceInstanceKey, tcDataProvider Database) {		super("GENERICLDAP.PROV", Database);		isInitialized = false;		logger.debug("Entering ::ldapUserOperations()");		logger.debug("ITResourceInstanceKey = " + Long.toString(ITResourceInstanceKey));		init(ITResourceInstanceKey);		logger.debug("Exiting ::ldapUserOperations()");	}	/**	 * This contains the common initialized code used by all constructors for this object.	 *	 * @param ITResourceInstanceKey This key value points to the IT resource instance.	 */	private void init(long ITResourceInstanceKey) {		logger.debug("Entering ::init()");		logger.debug("ITResourceInstanceKey = " + Long.toString(ITResourceInstanceKey));		try {			parsingUtil = new OIMLdapParsingUtils(logger);			Map serverInfo = getITResData(ITResourceInstanceKey);			String server = (String) serverInfo.get("Server Address");			int port = Integer.parseInt((String) serverInfo.get("Port"));			rootDN = (String) serverInfo.get("Root DN");			String bindPrincipal = (String) serverInfo.get("Bind Principal");			String bindCredentials = (String) serverInfo.get("Bind Credentials");			String bindType = (String) serverInfo.get("Bind Type");			String useSSL = (String) serverInfo.get("SSL");			Map parameters = getLookupValues((String) serverInfo.get("Parameter Lookup Code"));			userCreationBaseDN = (String) serverInfo.get("User Creation Base DN");			ldap = new OIMLdapServer(server, port, rootDN, bindPrincipal, bindCredentials, bindType, useSSL, parameters);			if (userCreationBaseDN == null || userCreationBaseDN.length() == 0) {				userCreationBaseDN = rootDN;			}			firstNameAttribute = ldap.lookupParameter("firstNameAttribute");			logger.debug("firstNameAttribute = " + firstNameAttribute);			lastNameAttribute = ldap.lookupParameter("lastNameAttribute");			logger.debug("lastNameAttribute = " + lastNameAttribute);			loginAttribute = ldap.lookupParameter("loginAttribute");			logger.debug("loginAttribute = " + loginAttribute);			try {				userSearchAttribute = ldap.lookupParameter("userSearchAttribute");			} catch (OIMLdapBaseException e) {				userSearchAttribute = loginAttribute;			}			logger.debug("userSearchAttribute = " + userSearchAttribute);			passwordAttribute = ldap.lookupParameter("passwordAttribute");			logger.debug("passwordAttribute = " + passwordAttribute);			objectClassAttribute = ldap.lookupParameter("objectClassAttribute");			logger.debug("objectClassAttribute = " + objectClassAttribute);			defaultUserObjectClass = ldap.lookupParameter("defaultUserObjectClass");			logger.debug("defaultUserObjectClass = " + defaultUserObjectClass);			defaultGroupObjectClass = ldap.lookupParameter("defaultGroupObjectClass");			logger.debug("defaultGroupObjectClass = " + defaultGroupObjectClass);			defaultUserObjectClasses = expandMultiValues(ldap.lookupParameter("defaultUserObjectClasses"));			useDnForMember = ldap.lookupParameter("useDnForMember").equalsIgnoreCase("true");			logger.debug("useDnForMember = " + Boolean.toString(useDnForMember));			groupMemberAttribute = ldap.lookupParameter("groupMemberAttribute");			logger.debug("groupMemberAttribute = " + groupMemberAttribute);			cnAttribute = ldap.lookupParameter("cnAttribute");			logger.debug("cnAttribute = " + cnAttribute);			primaryUserAttribute = ldap.lookupParameter("primaryUserAttribute");			logger.debug("primaryUserAttribute = " + primaryUserAttribute);			dnAttribute = ldap.lookupParameter("dnAttribute");			logger.debug("dnAttribute = " + dnAttribute);			ouAttribute = ldap.lookupParameter("ouAttribute");			logger.debug("ouAttribute = " + ouAttribute);			ouObjectClass = ldap.lookupParameter("ouObjectClass");			logger.debug("ouObjectClass = " + ouObjectClass);			groupIDAttribute = ldap.lookupParameter("groupIDAttribute");			logger.debug("groupIDAttribute = " + groupIDAttribute);			try {				groupSearchAttribute = ldap.lookupParameter("groupSearchAttribute");			} catch (OIMLdapBaseException e) {				groupSearchAttribute = groupIDAttribute;			}			logger.debug("groupSearchAttribute = " + groupSearchAttribute);			isInitialized = true;		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		}		logger.debug("Exiting ::init()");	}	/**	 * This expands a semi-colon seperated string of items into an array.	 *	 * @param Input The string to parse in an array.	 * @return An array of items derived from the input.	 */	protected String[] expandMultiValues(String Input) {		logger.debug("Entering ::expandMultiValues()");		logger.debug("Input = " + Input);		String[] result = Input.split(";");		for (int i = 0; i < result.length; i++) {			logger.debug("result[] = " + result[i]);		}		logger.debug("Exiting ::expandMultiValues()");		return result;	}	/**	 * This builds a full DN for a new user.	 *	 * @param Container The DN of the container to create the user in.	 * @param RdnValue The value which will be used to create the RDN.	 * @return The DN for the new user.	 */	protected String buildNewUserDN(String Container, String RdnValue) {		logger.debug("Entering ::buildNewUserDN()");		logger.debug("Container = " + Container);		logger.debug("RdnValue = " + RdnValue);		String result = primaryUserAttribute + "=" + parsingUtil.escapeDNComponent(RdnValue) + "," + Container;		logger.debug("result = " + result);		logger.debug("Exiting ::buildNewUserDN()");		return result;	}	/**	 * Gets the parent object of a given DN.	 *	 * @param DN The DN to parse.	 * @return The parent object DN for a given object.	 */	protected String getParentObject(String DN) {		logger.debug("Entering ::getParentObject()");		logger.debug("DN = " + DN);		String result = parsingUtil.getParentDN(DN);		logger.debug("result = " + result);		logger.debug("Exiting ::getParentObject()");		return result;	}	/**	 * Gets the relative component of a given DN.	 *	 * @param DN The to parse.	 * @return The RDN for the given object.	 */	protected String getRelativeDN(String DN) {		logger.debug("Entering ::getRelativeDN()");		logger.debug("DN = " + DN);		String result = parsingUtil.escapeDNComponent(parsingUtil.getRelativeDN(DN));		logger.debug("result = " + result);		logger.debug("Exiting ::getRelativeDN()");		return result;	}	/**	 * Checks if a given DN exists.	 * 	 * @param DN The DN to test for presence.	 * @return <code>true</code> if the object exists, or <code>false</code> if the object does not.	 * @exception OIMLdapBaseException	 */	protected boolean objectExists(String DN) throws OIMLdapBaseException {		logger.debug("Entering ::objectExists()");		logger.debug("DN = " + DN);		boolean result = false;		try {			result = ldap.objectExists(DN);		} catch (OIMLdapObjectNotFoundException e) {			logger.debug(DN + " was not found");		}		logger.debug("result = " + Boolean.toString(result));		logger.debug("Exiting ::objectExists()");		return result;	}	/**	 * Returns the value (right hand side) for a given RDN.	 * 	 * @param RelativeDN The RDN to obtain the value for.	 * @return The value for the given RDN.	 */	protected String getRelativeDNValue(String RelativeDN) {		logger.debug("Entering ::getRelativeDNValue()");		logger.debug("RelativeDN = " + RelativeDN);		String result = "";		if (RelativeDN.indexOf("=") >= 0 && RelativeDN.indexOf("=") + 1 < RelativeDN.length()) {			result = RelativeDN.substring(RelativeDN.indexOf("=") + 1);		}		logger.debug("result = " + result);		logger.debug("Exiting ::getRelativeDNValue()");		return result;	}	/**	 * Creates an organizational unit container within a given parent DN.	 *	 * @param RelativeDN The RDN of the container to create.	 * @param ParentDN The DN to create the container under.	 * @exception OIMLdapBaseException	 */	protected void createSingleContainer(String RelativeDN, String ParentDN) throws OIMLdapBaseException {		logger.debug("Entering ::createSingleContainer()");		logger.debug("RelativeDN = " + RelativeDN);		logger.debug("ParentDN = " + ParentDN);		String dn = RelativeDN + "," + ParentDN;		String ouName = getRelativeDNValue(RelativeDN);		OIMLdapObject attributes = new OIMLdapObject();		attributes.setAttribute(objectClassAttribute, ouObjectClass);		attributes.setAttribute(ouAttribute, ouName);		ldap.createObject(dn, attributes);		logger.debug("Exiting ::createSingleContainer()");	}	/**	 * Creates containers (OU's) recursively as needed to build a parent DN out.	 *	 * @param DN The DN to build out using OU's.	 * @exception OIMLdapBaseException	 */	protected void createContainerIfNeeded(String DN) throws OIMLdapBaseException {		logger.debug("Entering ::createContainerIfNeeded()");		logger.debug("DN = " + DN);		String dn = DN;		Stack toCreate = new Stack();		boolean found = objectExists(dn);		while (!found) {			toCreate.push(getRelativeDN(dn));			dn = getParentObject(dn);			if (dn.length() == 0) {				throw new OIMLdapObjectNotFoundException("Unable to locate a base object");			}			found = objectExists(dn);		}		while (!toCreate.empty()) {			String relativeDN = (String) toCreate.pop();			createSingleContainer(relativeDN, dn);			if (dn.length() > 0) {				dn = relativeDN + "," + dn;			} else {				dn = relativeDN;			}		}		logger.debug("Exiting ::createContainerIfNeeded()");	}	/**	 * Adds a member to a group object.   This will silently succeed if the user is already a member of the group.	 *	 * @param Group The identifying value of the group object.	 * @param MemberAttribute The name of the membership attribute on the group object.	 * @param Username The identifying attribute of the user to add to the group.	 * @return         <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   Details 	 *                 about any operational failure are logged to the system logger as error messages.	 * @exception OIMLdapBaseException	 */	protected String addMember(String Group, String MemberAttribute, String Username) throws OIMLdapBaseException {		logger.debug("Entering ::addMember()");		logger.debug("Group = " + Group);		logger.debug("MemberAttribute = " + MemberAttribute);		logger.debug("Username = " + Username);		String result = "FAILURE";		String newMember = Username;		if (useDnForMember) {			newMember = getUserDN(Username);		}		Set currentMembers = getGroupAttribute(Group, MemberAttribute, true);		if (!currentMembers.contains(newMember.toLowerCase())) {			OIMLdapObject attributes = new OIMLdapObject();			attributes.setAttribute(MemberAttribute, newMember);			ldap.appendAttributeValue(getGroupDN(Group), attributes);			logger.info("Successfully added " + Username + " to " + Group);		} else {			logger.debug("Member " + newMember + " is already present, skipping addition");		}		result = "SUCCESS";		logger.debug("result = " + result);		logger.debug("Exiting ::addMember()");		return result;	}	/**	 * Removes a member from a group object.  This will silently succeed if the user is not a member of the group.	 *	 * @param Group The identifying value of the group object.	 * @param MemberAttribute The name of the membership attribute on the group object.	 * @param Username The identifying attribute of the user to add to the group.	 * @return         <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   Details 	 *                 about any operational failure are logged to the system logger as error messages.	 * @exception OIMLdapBaseException	 */	protected String removeMember(String Group, String MemberAttribute, String Username) throws OIMLdapBaseException {		logger.debug("Entering ::removeMember()");		logger.debug("Group = " + Group);		logger.debug("MemberAttribute = " + MemberAttribute);		logger.debug("Username = " + Username);		String result = "FAILURE";		String removeMember = Username;		if (useDnForMember) {			removeMember = getUserDN(Username);		}		Set currentMembers = getGroupAttribute(Group, MemberAttribute, true);		if (currentMembers.contains(removeMember.toLowerCase())) {			OIMLdapObject attributes = new OIMLdapObject();			attributes.setAttribute(MemberAttribute, removeMember);			ldap.removeAttributeValues(getGroupDN(Group), attributes);			logger.info("Successfully removed " + Username + " from " + Group);		} else {			logger.debug("Member " + removeMember + " is not present, skipping removal");		}		result = "SUCCESS";		logger.debug("result = " + result);		logger.debug("Exiting ::removeMember()");		return result;	}	/**	 * Replaces one member with another on all group objects.   This primarily used for user renames.	 * 	 * @param Old The user identifying attribute to remove.	 * @param New The user identitying attribute to add.	 * @exception OIMLdapBaseException	 */	protected void updateMemberAttribute(String Old, String New) throws OIMLdapBaseException {		logger.debug("Entering ::updateMemberAttribute()");		logger.debug("Old = " + Old);		logger.debug("New = " + New);		String[] types = new String[] { groupMemberAttribute };		for (int i = 0; i < types.length; i++) {			OIMLdapResultSet results = ldap.search(rootDN, "(&(" + objectClassAttribute + "=" +						defaultGroupObjectClass + ")(" + types[i] + "=" +						Old + "))");			String[] keys = results.getKeys();			for (int j = 0; j < keys.length; j++) {				logger.debug("Updating " + keys[j]);				try {					OIMLdapObject attributes = new OIMLdapObject();					attributes.setAttribute(types[i], Old);					ldap.removeAttributeValues(keys[j], attributes);					attributes = new OIMLdapObject();					attributes.setAttribute(types[i], New);					ldap.appendAttributeValue(keys[j], attributes);				} catch (Exception e) {					logger.warn(e.getMessage());				}			}		}		logger.debug("Exiting ::updateMemberAttribute()");	}	/**	 * Gets a Set of one or more values for a specific attribute on a user object.	 *	 * @param Name The identify value for finding the user object	 * @param Attribute The name of the ldap attribute to retrieve the values for.	 * @param ToLower <code>true</code> if values should be converted to lowercase, or <code>false</code>	 *                if the values should be preserved.	 * @return A Set containg zero or more values for the specified attribute. 	 */	protected Set getUserAttribute(String Name, String Attribute, boolean ToLower) {		logger.debug("Entering ::getUserAttribute()");		logger.debug("Name = " + Name);		logger.debug("Attribute = " + Attribute);		logger.debug("ToLower = " + Boolean.toString(ToLower));		Set result = getObjectAttribute(rootDN, "(&(" + objectClassAttribute + "=" + defaultUserObjectClass +						")(" + userSearchAttribute + "=" + Name + "))", Attribute, ToLower);		logger.debug("Found " + result.size() + " attribute values for " + Attribute);		logger.debug("Exiting ::getUserAttribute()");		return result;	}	/**	 * Gets a Set of one or more values for a specific attribute on a group object.	 *	 * @param Name The identify value for finding the group object	 * @param Attribute The name of the ldap attribute to retrieve the values for.	 * @param ToLower <code>true</code> if values should be converted to lowercase, or <code>false</code>	 *                if the values should be preserved.	 * @return A Set containg zero or more values for the specified attribute. 	 * @exception OIMLdapBaseException	 */	protected Set getGroupAttribute(String Name, String Attribute, boolean ToLower) throws OIMLdapBaseException {		logger.debug("Entering ::getGroupAttribute()");		logger.debug("Name = " + Name);		logger.debug("Attribute = " + Attribute);		logger.debug("ToLower = " + Boolean.toString(ToLower));		Set result = new HashSet();		if (ldap.objectExists(Name)) {			result = ldap.getObject(Name, new String[] {Attribute}, true).getAttributeSet(Attribute);		} else {			String filter = "(&(" + objectClassAttribute + "=" + defaultGroupObjectClass;			filter += ")(" + groupSearchAttribute + "=" + Name + "))";			result = getObjectAttribute(rootDN, filter, Attribute, ToLower);		}		logger.debug("Found " + result.size() + " attribute values for " + Attribute);		logger.debug("Exiting ::getGroupAttribute()");		return result;	}	/**	 * Gets a Set of one or more values for a specific attribute on an object.	 *	 * @param RootDN The DN which to searhc under for matching objects.	 * @param Filter The LDAP search filter for finding the object.  This search filter must return exactly one 	 *               object, or no values will be returned by this function.	 * @param Attribute The name of the ldap attribute to retrieve the values for.	 * @param ToLower <code>true</code> if values should be converted to lowercase, or <code>false</code>	 *                if the values should be preserved.	 * @return A Set containg zero or more values for the specified attribute. 	 */	protected Set getObjectAttribute(String RootDN, String Filter, String Attribute, boolean ToLower) {		logger.debug("Entering ::getObjectAttribute()");		logger.debug("RootDN = " + RootDN);		logger.debug("Filter = " + Filter);		logger.debug("Attribute = " + Attribute);		logger.debug("ToLower = " + Boolean.toString(ToLower));		Set result = new TreeSet();		try {			OIMLdapResultSet results = ldap.search(RootDN, Filter, new String[] { Attribute }, ToLower);			String[] keys = results.getKeys();			if (keys.length == 1) {				OIMLdapObject entry = results.getObject(keys[0]);				if (entry.containsAttribute(Attribute.toLowerCase())) {					result = entry.getAttributeSet(Attribute.toLowerCase());				}			} else {				logger.debug("A non-singular result was obtained for " + Filter + "; nothing will be returned");			}		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		}		logger.debug("Exiting ::getObjectAttribute()");		return result;	}		/**	 * Gets a Set of the userSearchFilter attribute for all objects matching the filter.	 *	 * @param Filter The LDAP search filter for finding the object.	 * @return A Set containg zero or more values for the specified attribute. 	 */	public Set getMatchingUsers(String Filter) {		Set result = new TreeSet();		if (!isInitialized) {			return result;		}		logger.debug("Entering ::getMatchingUsers)");		logger.debug("Filter = " + Filter);		try {			ldap.connect();			OIMLdapResultSet results = ldap.search(rootDN, Filter, new String[] { userSearchAttribute }, false);			String[] keys = results.getKeys();			for (int i = 0; i < keys.length; i++) {				OIMLdapObject entry = results.getObject(keys[0]);				if (entry.containsAttribute(userSearchAttribute.toLowerCase())) {					result.add(entry.getAttribute(userSearchAttribute.toLowerCase())[0]);				}			}		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("Exiting ::getMatchingUsers()");		return result;	}	/**	 * Given a username to find, this retrieves a Set containing zero or more values for the requested attribute.	 *	 * @param Username  The username or other identifying attribute of the user.	 * @param Attribute The name of the attribute to retrieve values for.   	 * @return          A Set containing attribute values.  If the attribute is not found, or no values 	 *                  are present, then an empty, non-null Set will be returned.	 */	public Set getUserAttribute(String Username, String Attribute) {		Set result = new TreeSet();		if (!isInitialized) {			return result;		}		logger.debug("Entering ::getUserAttribute()");		logger.debug("Username = " + Username);		logger.debug("Attribute = " + Attribute);		try {			ldap.connect();			result = getUserAttribute(Username, Attribute, false);		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("Exiting ::getUserAttribute()");		return result;	}	/**	 * Given a username to find, this retrieves a Set containing zero or more values for the requested attribute.	 *	 * @param Group     The group name or other identifying attribute of the group.	 * @param Attribute The name of the attribute to retrieve values for.   	 * @return          A Set containing attribute values.  If the attribute is not found, or no values 	 *                  are present, then an empty, non-null Set will be returned.	 */	public Set getGroupAttribute(String Group, String Attribute) {		Set result = new TreeSet();		if (!isInitialized) {			return result;		}		logger.debug("Entering ::getGroupAttribute()");		logger.debug("Group = " + Group);		logger.debug("Attribute = " + Attribute);		try {			ldap.connect();			result = getGroupAttribute(Group, Attribute, false);		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("Exiting ::getGroupAttribute()");		return result;	}	/**	 * Given an LDAP search filter to find, this retrieves a Set containing zero or more values for the requested attribute.	 *	 * @param Filter    The filter used to find a singular object within the LDAP directory.	 * @param Attribute The name of the attribute to retrieve values for.   	 * @return          A Set containing attribute values.  If the attribute is not found, or no values 	 *                  are present, then an empty, non-null Set will be returned.	 */	public Set getObjectAttribute(String Filter, String Attribute) {		Set result = new TreeSet();		if (!isInitialized) {			return result;		}		logger.debug("Entering ::getObjectAttribute()");		logger.debug("Filter = " + Filter);		logger.debug("Attribute = " + Attribute);		try {			ldap.connect();			result = getObjectAttribute(rootDN, Filter, Attribute, false);		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("Exiting ::getObjectAttribute()");		return result;	}	/**	 * Encodes the given password string into a Unicode byte array, and sets the resulting value on the specified user account.	 *	 * @param Username The username or other identifying attribute of the user.	 * @param Password The new password to set for the users.	 * @return         <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   Details 	 *                 about any operational failure are logged to the system logger as error messages.	 */	public String setUnicodePassword(String Username, String Password) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::setUnicodePassword()");		logger.debug("Username = " + Username);		try {			ldap.connect();			String dn = getUserDN(Username);			if (dn == null || dn.length() == 0) {				throw new OIMLdapObjectNotFoundException(Username);			}			String quotedValue = "\"" + Password + "\"";			char chars[] = quotedValue.toCharArray();			byte bytes[] = new byte[chars.length*2];			for (int i = 0; i < chars.length; i++) {				bytes[i * 2 + 1] = (byte) (chars[i] >>> 8);				bytes[i * 2 + 0] = (byte) (chars[i] & 0xff);			}			ldap.setBinaryAttribute(dn, passwordAttribute, bytes);			result = "SUCCESS";		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::setUnicodePassword()");		return result;	}	/**	 * Searches the LDAP directory for a single object, and returns the first value found for the given attribute.	 * The specified LDAP search must return exactly one object for the method to succeed.	 *	 * @param Filter    An LDAP search filter that returns exactly one object.	 * @param Attribute The name of the attribute to retrieve for the single resulting object.  If the specified 	 *                  attribute contains multiple values, only the first value found is returned.	 * @return          This returns the first value for the given attribute on the object found by the specified filter.	 *                  A non-null, empty string is returned if the either the object or the attribute is not found.	 */	public String getObjectSingleAttribute(String Filter, String Attribute) {		logger.debug("Entering ::getObjectSingleAttribute()");		logger.debug("Filter = " + Filter);		logger.debug("Attribute = " + Attribute);		String result = getObjectSingleAttribute(Filter, Attribute, rootDN);		logger.debug("result = " + result);		logger.debug("Exiting ::getObjectSingleAttribute()");		return result;	}	/**	 * Searches the LDAP directory for a single object, and returns the first value found for the given attribute.	 * The specified LDAP search must return exactly one object for the method to succeed.	 *	 * @param Filter     An LDAP search filter that returns exactly one object.	 * @param Attribute  The name of the attribute to retrieve for the single resulting object.  If the specified 	 *                   attribute contains multiple values, only the first value found is returned.	 * @param SearchBase The DN in which the LDAP search will be based.	 * @return           This returns the first value for the given attribute on the object found by the specified filter.	 *                   A non-null, empty string is returned if the either the object or the attribute is not found.	 */	public String getObjectSingleAttribute(String Filter, String Attribute, String SearchBase) {		String result = "";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::getObjectSingleAttribute()");		logger.debug("Filter = " + Filter);		logger.debug("Attribute = " + Attribute);		logger.debug("SearchBase = " + SearchBase);		try {			ldap.connect();			Set results = getObjectAttribute(SearchBase, Filter, Attribute, false);			if (results.size() >= 1) {				result = (String) results.toArray(new String[1])[0];				if (results.size() > 1) {					logger.warn("Multiple values were found for " + Attribute);					logger.warn("Only the first value found is being returned");				}			} else {				logger.debug("No results for found for " + Attribute);			}		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::getObjectSingleAttribute()");		return result;	}		/**	 * Searches the LDAP directory for a single object, and set the specified attribute to the specified value.	 * The specified LDAP search must return exactly one object for the method to succeed.	 *	 * @param Filter     An LDAP search filter that returns exactly one object.	 * @param Attribute  The name of the attribute to set for the single resulting object.	 * @param Value      The value to set for the specified attribute.	 * @return           <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation 	 *                   fails.   Details about any operational failure are logged to the system logger as 	 *                   error messages.	 */	public String setObjectSingleAttribute(String Filter, String Attribute, String Value) {		logger.debug("Entering ::setObjectSingleAttribute()");		logger.debug("Filter = " + Filter);		logger.debug("Attribute = " + Attribute);		logger.debug("Value = " + Value);		String result = setObjectSingleAttribute(Filter, Attribute, Value, rootDN);		logger.debug("result = " + result);		logger.debug("Exiting ::setObjectSingleAttribute()");		return result;	}		/**	 * Searches the LDAP directory for a single object, and set the specified attribute to the specified value.	 * If the filter returns multiple objects, then all of those objects will be updated with the new attribute	 * and value.	 *	 * @param Filter     An LDAP search filter that returns exactly one object.	 * @param Attribute  The name of the attribute to set for the single resulting object.	 * @param Value      The value to set for the specified attribute.	 * @param SearchBase The DN in which the LDAP search will be based.	 * @return           <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation 	 *                   fails.   Details about any operational failure are logged to the system logger as 	 *                   error messages.	 */	public String setObjectSingleAttribute(String Filter, String Attribute, String Value, String SearchBase) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::setObjectSingleAttribute()");		logger.debug("Filter = " + Filter);		logger.debug("Attribute = " + Attribute);		logger.debug("Value = " + Value);		logger.debug("SearchBase = " + SearchBase);		try {			ldap.connect();			Set results = getObjectAttribute(SearchBase, Filter, dnAttribute, false);			String[] dn = (String[]) results.toArray(new String[0]);			for (int i = 0; i < results.size(); i++) {				OIMLdapObject values = new OIMLdapObject();				values.setAttribute(Attribute, Value);				ldap.setAttributeValue(dn[i], values);				result = "SUCCESS";			}		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::setObjectSingleAttribute()");		return result;	}	/**	 * This creates a new object whose parameters (including objectclasses) are defined in the specified	 * ldapObject parameter.	 *	 * @param OU                   The full DN value of the OU to create the new object in.	 * @param PrimaryAttributeName The name of the LDAP attribute to be used to form the relative component	 *                             of the new object's DN.  As an example, if the new object's relative DN should	 *                             be <code>cn=MyObject</code>, then one would specify <code>cn</code> for this 	 *                             parameter.   The object passed into the Item paramater must contain a value for 	 *                             the attribute named here.	 * @param Item                 This is an object containing the attribute names and values of the new object to	 *                             create.   This must contain objectclass information for the new object, as well	 *                             as a name/value pair for the PrimaryAttributeName.	 * @return                     <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation 	 *                             fails.   Details about any operational failure are logged to the system logger as 	 *                             error messages.	 */	public String createObjectInOU(String OU, String PrimaryAttributeName, OIMLdapObject Item) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::createObjectInOU()");		logger.debug("OU = " + OU);		logger.debug("PrimaryAttributeName = " + PrimaryAttributeName);		try {			ldap.connect();			createContainerIfNeeded(OU);			String dn = PrimaryAttributeName + "=" + Item.getAttribute(PrimaryAttributeName)[0] + "," + OU;			if (!objectExists(dn)) {				ldap.createObject(dn, Item);				result = "SUCCESS";			} else {				logger.debug("Skipping object creation... already exists");				result = "SUCCESS";			}		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::createObjectInOU()");		return result;	}	/**	 * Searches the LDAP directory for a single object, and removes that object from the directory.	 *	 * @param Filter    An LDAP search filter that returns exactly one object.	 * @return          <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                  Details about any operational failure are logged to the system logger as error messages.	 */	public String removeObject(String Filter) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::removeObject()");		logger.debug("Filter = " + Filter);		try {			ldap.connect();			String dn = getObjectSingleAttribute(Filter, dnAttribute);			if (objectExists(dn)) {				ldap.removeObject(dn);				result = "SUCCESS";			} else {				logger.debug("Skipping object removal... doesn't exist");				result = "SUCCESS";			}		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::removeObject()");		return result;	}	/**	 * Given a username or other uniquely identifying parameter, gets the DN for the corresponding object.	 *	 * @param Name The username or other identifying attribute of the user.	 * @return     Returns the DN of the corresponding object, or an empty, non-null string if no matching 	 *             object is found.	 */	public String getUserDN(String Name) {		String result = "";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::getUserDN()");		logger.debug("Name = " + Name);		try {			ldap.connect();			OIMLdapResultSet results = ldap.search(rootDN, "(&(" + objectClassAttribute + "=" + defaultUserObjectClass +						")(" + userSearchAttribute + "=" + Name + "))", new String[] { dnAttribute });			String[] keys = results.getKeys();			if (keys.length == 1) {				result = keys[0];			}		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::getUserDN()");		return result;	}	/**	 * Given a group name or other uniquely identifying parameter, gets the DN for the corresponding object.	 *	 * @param Name The group name or other identifying attribute of the user.	 * @return     Returns the DN of the corresponding object, or an empty, non-null string if no matching 	 *             object is found.	 */	public String getGroupDN(String Name) {		String result = "";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::getGroupDN()");		logger.debug("Name = " + Name);		try {			ldap.connect();			if (ldap.objectExists(Name)) {				result = Name;			} else {				OIMLdapResultSet results = ldap.search(rootDN, "(&(" + objectClassAttribute + "=" + defaultGroupObjectClass +									")(" + groupSearchAttribute + "=" + Name + "))", new String[] { dnAttribute });				String[] keys = results.getKeys();				if (keys.length == 1) {					result = keys[0];				}			}		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::getGroupDN()");		return result;	}	/**	 * Creates a user account with the specified parameters.	 *	 * @param Firstname The first name of the user to be created.	 * @param Lastname  The last name of the user to be created.	 * @param Username  The username of the user to be created.	 * @param Password   The password for the user to be created.	 * @return          <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                  Details about any operational failure are logged to the system logger as error messages.	 */	public String createUser(String Firstname, String Lastname, String Username, String Password) {		logger.debug("Entering ::createUser()");		String result = createUserInOU(userCreationBaseDN, Firstname, Lastname, Username, Password);		logger.debug("Exiting ::createUser()");		return result;	}	/**	 * Creates a user account with the specified parameters.	 *	 * @param Firstname     The first name of the user to be created.	 * @param Lastname      The last name of the user to be created.	 * @param Username      The username of the user to be created.	 * @param Password       The password for the user to be created.	 * @param UseUnicodePwd If this is <code>true</code>, then the password is encoded as a unicode byte array.	 *                      If this is <code>false</code>, then the password is set without any additional modification.	 * @return              <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                      Details about any operational failure are logged to the system logger as error messages.	 */	public String createUser(String Firstname, String Lastname, String Username, String Password, boolean UseUnicodePwd) {		logger.debug("Entering ::createUser()");		String result = createUserInOU(userCreationBaseDN, Firstname, Lastname, Username, Password, UseUnicodePwd);		logger.debug("Exiting ::createUser()");		return result;	}	/**	 * Creates a user account with the specified parameters in the specified OU.	 *	 * @param OU        The full DN of the container to place the new account in.	 * @param Firstname The first name of the user to be created.	 * @param Lastname  The last name of the user to be created.	 * @param Username  The username of the user to be created.	 * @param Password   The password for the user to be created.	 * @return          <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                  Details about any operational failure are logged to the system logger as error messages.	 */	public String createUserInOU(String OU, String Firstname, String Lastname, String Username, String Password) {		logger.debug("Entering ::createUserInOU()");		String result = createUserInOU(OU, Firstname, Lastname, Username, Password, false);		logger.debug("Exiting ::createUserInOU()");		return result;	}		/**	 * Creates a user account with the specified parameters in the specified OU.	 *	 * @param OU            The full DN of the container to place the new account in.	 * @param Firstname     The first name of the user to be created.	 * @param Lastname      The last name of the user to be created.	 * @param Username      The username of the user to be created.	 * @param Password       The password for the user to be created.	 * @param UseUnicodePwd If this is <code>true</code>, then the password is encoded as a unicode byte array.	 *                      If this is <code>false</code>, then the password is set without any additional modification.	 * @return              <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                      Details about any operational failure are logged to the system logger as error messages.	 */	public String createUserInOU(String OU, String Firstname, String Lastname, String Username, String Password, boolean UseUnicodePwd) {		logger.debug("Entering ::createUserInOU()");		String result = createUserInOU(OU, Firstname, Lastname, Username, Password, UseUnicodePwd, Username);		logger.debug("Exiting ::createUserInOU()");		return result;	}	/**	 * Creates a user account with the specified parameters in the specified OU.	 *	 * @param OU            The full DN of the container to place the new account in.	 * @param Firstname     The first name of the user to be created.	 * @param Lastname      The last name of the user to be created.	 * @param Username      The username of the user to be created.	 * @param Password       The password for the user to be created.	 * @param UseUnicodePwd If this is <code>true</code>, then the password is encoded as a unicode byte array.	 *                      If this is <code>false</code>, then the password is set without any additional modification.	 * @param ContainerName The name of the container RDN to use for the new user account.	 * @return              <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                      Details about any operational failure are logged to the system logger as error messages.	 */	public String createUserInOU(String OU, String Firstname, String Lastname, String Username, String Password, boolean UseUnicodePwd, String ContainerName) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::createUserInOU()");		logger.debug("OU = " + OU);		logger.debug("Firstname = " + Firstname);		logger.debug("Lastname = " + Lastname);		logger.debug("Username = " + Username);		logger.debug("Password = **********");		logger.debug("UseUnicodePwd = " + Boolean.toString(UseUnicodePwd));		logger.debug("ContainerName = " + ContainerName);		try {			logger.info("Entology Generic LDAP Connector -- (C) 2006-2008 Entology, Inc.");			logger.info("Permitted usage is strictly restricted to purposes explicitly granted by Entology, Inc.");			ldap.connect();			if (OU == null || OU.length() == 0) {				OU = userCreationBaseDN;			}			createContainerIfNeeded(OU);			String dn = buildNewUserDN(OU, ContainerName);			if (!objectExists(dn)) {				OIMLdapObject attributes = new OIMLdapObject();				attributes.setAttribute(primaryUserAttribute, ContainerName);				if (!primaryUserAttribute.equalsIgnoreCase(loginAttribute)) {					attributes.setAttribute(loginAttribute, Username);				}				attributes.setAttribute(firstNameAttribute, Firstname);				attributes.setAttribute(lastNameAttribute, Lastname);				if (!UseUnicodePwd) {					attributes.setAttribute(passwordAttribute, Password);				}				attributes.setAttribute(objectClassAttribute, defaultUserObjectClasses);				ldap.createObject(dn, attributes);				result = "SUCCESS";				logger.info("Successfully provisioned an account for " + Username);				if (UseUnicodePwd) {					String oldSearchAttribute = userSearchAttribute;					userSearchAttribute = loginAttribute;					result = setUnicodePassword(Username, Password);					userSearchAttribute = oldSearchAttribute;				}			} else {				logger.debug("Skipping object creation... already exists");				result = "SUCCESS";			}		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::createUserInOU()");		return result;	}	/**	 * Deletes the account for a user.	 *	 * @param Username The username or other uniquely identifying attribute for the user to be deleted.	 * @return         <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                 Details about any operational failure are logged to the system logger as error messages.	 */	public String deleteUser(String Username) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::deleteUser()");		logger.debug("Username = " + Username);		try {			ldap.connect();			String dn = getUserDN(Username);			if (dn != null && dn.length() > 0) {				removeUserFromAllGroups(Username);				ldap.removeObject(dn);				logger.info("Successfully removed the account " + Username);			}			result = "SUCCESS";		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::deleteUser()");		return result;	}	/** 	 * Renames only the DN of a user.	 * 	 * @param Username The username or other uniquely identifying attribute for the user to be deleted.	 * @param NewDn    The new DN for the account	 * @return         <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                 Details about any operational failure are logged to the system logger as error messages.	 */	public synchronized String renameUserDn(String Username, String NewDn) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::renameUserDn()");		logger.debug("Username = " + Username);		logger.debug("NewDn = " + NewDn);		try {			ldap.connect();			String dn = getUserDN(Username);			if (dn == null || dn.length() == 0) {				throw new OIMLdapObjectNotFoundException(Username);			}			String oldDN = getUserDN(Username);			String container = getParentObject(oldDN);			String newDN = buildNewUserDN(container, NewDn);			if (oldDN.equals(newDN)) {				logger.debug("No change in username detected; ignoring request");				result = "SUCCESS";			} else {				ldap.renameObject(oldDN, newDN);				if (useDnForMember) {					updateMemberAttribute(oldDN, newDN);				}				logger.info("Successfully renamed the account DN " + oldDN + " to " + newDN);				result = "SUCCESS";			}		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::renameUser()");		return result;	}                                                       	/**	 * Renames the account for a user.	 *	 * @param Username    The username or other uniquely identifying attribute for the user to be deleted.	 * @param NewUsername The new username for the account.	 * @return            <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                    Details about any operational failure are logged to the system logger as error messages.	 */	public synchronized String renameUser(String Username, String NewUsername) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::renameUser()");		logger.debug("Username = " + Username);		logger.debug("NewUsername = " + NewUsername);		try {			ldap.connect();			String dn = getUserDN(Username);			if (dn == null || dn.length() == 0) {				throw new OIMLdapObjectNotFoundException(Username);			}			String oldUsername = (String) getUserAttribute(Username, loginAttribute, false).toArray(new String[1])[0];			if (!oldUsername.equals(NewUsername)) {				if (oldUsername.equalsIgnoreCase(NewUsername)) {					logger.debug("Change in username case detected - renaming to a temporary value");					result = renameUser(Username, oldUsername + "-temp");					if (result.equals("SUCCESS")) {						if (userSearchAttribute.equalsIgnoreCase(loginAttribute)) {							result = renameUser(oldUsername + "-temp", NewUsername);						} else {							result = renameUser(Username, NewUsername);						}					}				} else {					String oldDN = getUserDN(Username);					String container = getParentObject(oldDN);					String newDN = buildNewUserDN(container, NewUsername);					if (!loginAttribute.equalsIgnoreCase(primaryUserAttribute)) {						Set loginValues = getUserAttribute(Username, loginAttribute, true);						if (loginValues.size() > 1) {							removeAttributeValue(Username, loginAttribute, oldUsername);							appendAttributeValue(Username, loginAttribute, NewUsername);						} else {							setAttributeValue(Username, loginAttribute, NewUsername);						}					}					ldap.renameObject(oldDN, newDN);					if (useDnForMember) {						updateMemberAttribute(oldDN, newDN);					} else {						updateMemberAttribute(oldUsername, NewUsername);					}					logger.info("Successfully renamed the account " + oldUsername + " to " + NewUsername);					result = "SUCCESS";				}			} else {				logger.debug("No change in username detected; ignoring request");				result = "SUCCESS";			}		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::renameUser()");		return result;	}	/**	 * Sets the value for a given attribute.   The attribute is assumed to contain a singular value.	 *	 * @param Username       The username or other uniquely identifying attribute for the user to be modified.	 * @param AttributeName  The name of the LDAP attribute to set.	 * @param AttributeValue The value to set for the attribute.  An empty or null value will clear any existing data	 *                       from the attribute.	 * @return               <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                       Details about any operational failure are logged to the system logger as error messages.	 */	public String setAttributeValue(String Username, String AttributeName, String AttributeValue) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::setAttributeValue()");		logger.debug("Username = " + Username);		logger.debug("AttributeName = " + AttributeName);		logger.debug("AttributeValue = " + AttributeValue);		try {			ldap.connect();			String dn = getUserDN(Username);			if (dn == null || dn.length() == 0) {				throw new OIMLdapObjectNotFoundException(Username);			}			OIMLdapObject attributes = new OIMLdapObject();			attributes.setAttribute(AttributeName, AttributeValue);			ldap.setAttributeValue(dn, attributes);			logger.info("Successfully set a new value for the attribute " + AttributeName + " on the account for " + Username);			result = "SUCCESS";		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::setAttributeValue()");		return result;	}	/**	 * Sets the value for a given attribute.   The attribute is assumed to contain a singular, binary value.	 * The input value is assumed to be a string of hex characters representing the binary value.  This is the	 * same format as is returned by recon when the binaryAttributes value is set to include the attribute.	 *	 * @param Username       The username or other uniquely identifying attribute for the user to be modified.	 * @param AttributeName  The name of the LDAP attribute to set.	 * @param AttributeValue The value to set for the attribute.  An empty or null value will clear any existing data	 *                       from the attribute.  The input value is assumed to be a string of hex characters 	 *                       representing the binary value.	 * @return               <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                       Details about any operational failure are logged to the system logger as error messages.	 */	public String setAttributeBlob(String Username, String AttributeName, String AttributeValue) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::setAttributeBlob()");		logger.debug("Username = " + Username);		logger.debug("AttributeName = " + AttributeName);		logger.debug("AttributeValue = " + AttributeValue);		try {			ldap.connect();			String dn = getUserDN(Username);			if (dn == null || dn.length() == 0) {				throw new OIMLdapObjectNotFoundException(Username);			}			byte[] blob = new byte[AttributeValue.length() / 2];			for (int i = 0; i < blob.length; i++) {				blob[i] = (byte) Integer.parseInt(AttributeValue.substring(i * 2, (i * 2) + 2), 16);			}			ldap.setBinaryAttribute(dn, AttributeName, blob);			logger.info("Successfully set a new value for the attribute " + AttributeName + " on the account for " + Username);			result = "SUCCESS";		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::setAttributeBlob()");		return result;	}	/**	 * Assumes that the given attribute is a bitmask, and updates a single bit on that attribute without	 * affecting the other bits in the value.   The attribute is assumed to contain a singular value.	 *	 * @param Username       The username or other uniquely identifying attribute for the user to be modified.	 * @param AttributeName  The name of the LDAP attribute to set.	 * @param AttributeMask  The mask used to isolate the desired bit on attribute.	 * @param AttributeValue Either <code>true</code> to set the bit, or <code>false</code> to unset the bit.	 * @return               <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                       Details about any operational failure are logged to the system logger as error messages.	 */	public String setAttributeBit(String Username, String AttributeName, int AttributeMask, boolean AttributeValue) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::setAttributeBit()");		logger.debug("Username = " + Username);		logger.debug("AttributeName = " + AttributeName);		logger.debug("AttributeMask = " + AttributeMask);		logger.debug("AttributeValue = " + AttributeValue);		try {			ldap.connect();			String dn = getUserDN(Username);			if (dn == null || dn.length() == 0) {				throw new OIMLdapObjectNotFoundException(Username);			}			OIMLdapObject attributes = new OIMLdapObject();			int value = 0;			Set values = getUserAttribute(Username, AttributeName, false);			if (values.size() > 0) {				value = Integer.parseInt((String) values.toArray(new String[0])[0]);			}			if (AttributeValue) {				value |= AttributeMask;			} else {				value &= ~AttributeMask;			}			attributes.setAttribute(AttributeName, Integer.toString(value));			ldap.setAttributeValue(dn, attributes);			logger.info("Successfully set a new value for the attribute " + AttributeName + " on the account for " + Username);			result = "SUCCESS";		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::setAttributeBit()");		return result;	}	/**	 * Appends an additional value for a given attribute.   The attribute may contain multiple values.	 *	 * @param Username       The username or other uniquely identifying attribute for the user to be modified.	 * @param AttributeName  The name of the LDAP attribute to set.	 * @param AttributeValue The value to append to the attribute.	 * @return               <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                       Details about any operational failure are logged to the system logger as error messages.	 */	public String appendAttributeValue(String Username, String AttributeName, String AttributeValue) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::appendAttributeValue()");		logger.debug("Username = " + Username);		logger.debug("AttributeName = " + AttributeName);		logger.debug("AttributeValue = " + AttributeValue);		try {			ldap.connect();			String dn = getUserDN(Username);			if (dn == null || dn.length() == 0) {				throw new OIMLdapObjectNotFoundException(Username);			}			OIMLdapObject attributes = new OIMLdapObject();			Set values = getUserAttribute(Username, AttributeName, true);			if (!values.contains(AttributeValue.toLowerCase())) {				attributes.setAttribute(AttributeName, AttributeValue);				ldap.appendAttributeValue(getUserDN(Username), attributes);				logger.info("Successfully added " + AttributeValue + " to the attribute " + AttributeName);			} else {				logger.debug(AttributeValue + " already exists in " + AttributeName + ", skipping.");			}			result = "SUCCESS";		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::appendAttributeValue()");		return result;	}	/**	 * Removes a value from a given attribute.   The attribute may contain multiple values.	 *	 * @param Username       The username or other uniquely identifying attribute for the user to be modified.	 * @param AttributeName  The name of the LDAP attribute to set.	 * @param AttributeValue The value to remove from the attribute.	 * @return               <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                       Details about any operational failure are logged to the system logger as error messages.	 */	public String removeAttributeValue(String Username, String AttributeName, String AttributeValue) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::removeAttributeValue()");		logger.debug("Username = " + Username);		logger.debug("AttributeName = " + AttributeName);		logger.debug("AttributeValue = " + AttributeValue);		try {			ldap.connect();			String dn = getUserDN(Username);			if (dn == null || dn.length() == 0) {				throw new OIMLdapObjectNotFoundException(Username);			}			OIMLdapObject attributes = new OIMLdapObject();			Set values = getUserAttribute(Username, AttributeName, true);			if (values.contains(AttributeValue.toLowerCase())) {				attributes.setAttribute(AttributeName, AttributeValue);				ldap.removeAttributeValues(getUserDN(Username), attributes);				logger.info("Successfully removed " + AttributeValue + " from the attribute " + AttributeName);			} else {				logger.debug(AttributeValue + " doesn't exist in " + AttributeName + ", skipping.");			}			result = "SUCCESS";		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::removeAttributeValue()");		return result;	}	/**	 * Removes a value from a given attribute.   The attribute may contain multiple values.	 *	 * @param Username The username or other uniquely identifying attribute for the user to be modified.	 * @param OU       The full DN of the container to move the user account to.	 * @return         <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                 Details about any operational failure are logged to the system logger as error messages.	 */	public String moveUserToOU(String Username, String OU) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::moveUserToOU()");		logger.debug("Username = " + Username);		logger.debug("OU = " + OU);		try {			ldap.connect();			String currentDN = getUserDN(Username);			if (currentDN == null || currentDN.length() == 0) {				throw new OIMLdapObjectNotFoundException(Username);			}			createContainerIfNeeded(OU);			logger.debug("currentDN = " + currentDN);			String newDN = getRelativeDN(currentDN) + "," + OU;			logger.debug("newDN = " + newDN);			if (!currentDN.equalsIgnoreCase(newDN)) {				ldap.renameObject(currentDN, newDN);				if (useDnForMember) {					updateMemberAttribute(currentDN, newDN);				}				logger.info("Successfully moved the account for " + Username + " to " + OU);			}			result = "SUCCESS";		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::moveUserToOU()");		return result;	}	/**	 * Adds a user to an LDAP group.	 *	 * @param Group    The name of the group to add the user to.	 * @param Username The username or other uniquely identifying attribute for the user to be added.	 * @return         <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                 Details about any operational failure are logged to the system logger as error messages.	 */	public String addUserToGroup(String Group, String Username) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::addUserToGroup()");		logger.debug("Group = " + Group);		logger.debug("Username = " + Username);		try {			ldap.connect();			String dn = getUserDN(Username);			if (dn == null || dn.length() == 0) {				throw new OIMLdapObjectNotFoundException(Username);			}			result = addMember(Group, groupMemberAttribute, Username);		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::addUserToGroup()");		return result;	}	/**	 * Removes a user from an LDAP group.	 *	 * @param Group    The name of the group to remove the user from.	 * @param Username The username or other uniquely identifying attribute for the user to be removed.	 * @return         <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                 Details about any operational failure are logged to the system logger as error messages.	 */	public String removeUserFromGroup(String Group, String Username) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::removeUserFromGroup()");		logger.debug("Group = " + Group);		logger.debug("Username = " + Username);		try {			ldap.connect();			String dn = getUserDN(Username);			if (dn == null || dn.length() == 0) {				throw new OIMLdapObjectNotFoundException(Username);			}			result = removeMember(Group, groupMemberAttribute, Username);		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::removeUserFromGroup()");		return result;	}	/**	 * Removes a user from all groups in which they are a member of.	 *	 * @param Username The username or other uniquely identifying attribute for the user to be removed.	 * @return         <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                 Details about any operational failure are logged to the system logger as error messages.	 */	public String removeUserFromAllGroups(String Username) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::removeUserFromAllGroups()");		logger.debug("Username = " + Username);		String item = Username;		try {			ldap.connect();			if (useDnForMember) {				item = getUserDN(Username);				if (item == null || item.length() == 0) {					throw new OIMLdapObjectNotFoundException(Username);				}			}			String[] types = new String[] { groupMemberAttribute };			for (int i = 0; i < types.length; i++) {				OIMLdapResultSet results = ldap.search(rootDN, "(&(" + objectClassAttribute + "=" + defaultGroupObjectClass +							")(" + types[i] + "=" + item + "))");				String[] keys = results.getKeys();				for (int j = 0; j < keys.length; j++) {					logger.debug("Updating " + keys[j]);					try {						OIMLdapObject attributes = new OIMLdapObject();						attributes.setAttribute(types[i], item);						ldap.removeAttributeValues(keys[j], attributes);					} catch (Exception e) {						logger.warn(e.getMessage());					}				}				result = "SUCCESS";			}		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::removeUserFromAllGroups()");		return result;	}	/**	 * Adds all the attributes and values specified in the ldap object to a given user.  Any attributes specified	 * in the change object will overwrite existing values on the user's account.	 *	 * @param Username The username or other uniquely identifying attribute for the user to be removed.	 * @param Item     An ldapObject containing the attributes and values to overwrite the existing entries.	 * @return         <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                 Details about any operational failure are logged to the system logger as error messages.	 */	public String processUserAttributeSet(String Username, Object Item) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::processUserAttributeSet()");		logger.debug("Username = " + Username);		try {			ldap.connect();			String dn = getUserDN(Username);			if (dn == null || dn.length() == 0) {				throw new OIMLdapObjectNotFoundException(Username);			}			ldap.setAttributeValue(dn, (OIMLdapObject) Item);			result = "SUCCESS";		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::processUserAttributeSet()");		return result;	}	/**	 * Adds all the attributes and values specified in the ldap object to a given user.  Any attributes specified	 * in the change object will be appended to any existing values on the user's account.	 *	 * @param Username The username or other uniquely identifying attribute for the user to be removed.	 * @param Item     An ldapObject containing the attributes and values to overwrite the existing entries.	 * @return         <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                 Details about any operational failure are logged to the system logger as error messages.	 */	public String processUserAttributeAddition(String Username, Object Item) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::processUserAttributeAddition()");		logger.debug("Username = " + Username);		try {			ldap.connect();			String dn = getUserDN(Username);			if (dn == null || dn.length() == 0) {				throw new OIMLdapObjectNotFoundException(Username);			}			ldap.appendAttributeValue(dn, (OIMLdapObject) Item);			result = "SUCCESS";		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::processUserAttributeAddition()");		return result;	}	/**	 * Removes all the attributes and values specified in the ldap object to a given user.  This method will	 * generate an error if any of the specified attribute / value pairs is not present.	 *	 * @param Username            The username or other uniquely identifying attribute for the user to be removed.	 * @param Item                An ldapObject containing the attributes and values to remove from the user's account.	 * @param IgnoreMissingObject If this is <code>true</code>, then this method will return success if the user's account	 *                            is not found.  If this is <code>false</code>, then an error will be generated if the user's	 *				  account is not found.	 * @return                    <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                            Details about any operational failure are logged to the system logger as error messages.	 */	public String processUserAttributeRemoval(String Username, Object Item, boolean IgnoreMissingObject) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::processUserAttributeRemoval()");		logger.debug("Username = " + Username);		try {			ldap.connect();			String dn = getUserDN(Username);			if (IgnoreMissingObject && (dn == null || dn.length() == 0)) {				result = "SUCCESS";			} else {				if (dn == null && dn.length() == 0) {					throw new OIMLdapObjectNotFoundException(Username);				}				ldap.removeAttributeValues(dn, (OIMLdapObject) Item);				result = "SUCCESS";			}		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::processUserAttributeRemoval()");		return result;	}	/**	 * Removes all the attributes and values specified in the ldap object to a given user.  This method will	 * not generate an error if any of the specified attribute / value pairs is not present, and will continue	 * removing the other specified attributes and values.	 *	 * @param Username The username or other uniquely identifying attribute for the user to be removed.	 * @param Item     An ldapObject containing the attributes and values to remove from the user's account.	 * @return         <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                 Details about any operational failure are logged to the system logger as error messages.	 */	public String processUserOptionalAttributeRemoval(String Username, Object Item) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::processUserOptionalAttributeRemoval()");		logger.debug("Username = " + Username);		try {			ldap.connect();			String dn = getUserDN(Username);			if (dn == null && dn.length() == 0) {				throw new OIMLdapObjectNotFoundException(Username);			}			result = "SUCCESS";			OIMLdapObject item = (OIMLdapObject) Item;			String[] keys = item.getKeys();			for (int i = 0; i < keys.length; i++) {				OIMLdapObject single = new OIMLdapObject();				single.setAttribute(keys[i], item.getAttribute(keys[i]));				try {					ldap.removeAttributeValues(dn, single);				} catch (OIMLdapBaseException e) {					logger.debug(e.getMessage());				}			}		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::processUserOptionalAttributeRemoval()");		return result;	}	/**	 * Retrieves a parameter value from the parameters lookup that was passed to as part of the ldap connection info.	 *	 * @param ParameterName The name of the parameter to retrieve a value for.	 * @return              The value of the specified parameter, or an empty, non-null string if the parameter was	 *                      not found.	 */	public String getLDAPParameter(String ParameterName) {		String result = "";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::getLDAPParameter()");		logger.debug("ParameterName = " + ParameterName);		try {			result = ldap.lookupParameter(ParameterName);		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		}		logger.debug("result = " + result);		logger.debug("Exiting ::getLDAPParameter()");		return result;	}	/**	 * Searches the LDAP directory for all values of a given numeric attribute, and returns a value not	 * currently used by any object.	 *	 * @param AttributeName The name of the LDAP attribute that needs to be made unique.	 * @param StartingValue The number to start searching from.	 * @return              The first unused value discovered.	 */	public String getUnusedValue(String AttributeName, int StartingValue) {		String result = "";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::getUnusedValue()");		logger.debug("AttributeName = " + AttributeName);		logger.debug("StartingValue = " + Integer.toString(StartingValue));		try {			int value = StartingValue;			ldap.connect();			while (true) {				String filter = "(" + AttributeName + "=" + value + ")";				OIMLdapResultSet results = ldap.search(rootDN, filter);				if (results.getKeys().length == 0) {					break;				} else {					value++;				}			}			result = Integer.toString(value);		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::getUnusedValue()");		return result;	}	/**	 * Given a base username, this function will append numbers to that username to create a unique name.	 *	 * @param Username  The starting point to use when generate a new username.	 * @param MaxLength The maximum length permitted for a username.  As digits are added to make the	 *                  username unique, the string specified for <code>Username</code> will be truncated 	 *                  to fit within the maximum length.	 * @return          A unique verison of the username.	 */	public String makeUsernameUnique(String Username, int MaxLength) {		String result = "";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::makeUsernameUnique()");		logger.debug("Username = " + Username);		logger.debug("MaxLength = " + Integer.toString(MaxLength));		try {			ldap.connect();			int number = 0;			result = Username;			if (Username.length() > MaxLength) {				Username = Username.substring(0, MaxLength);			}			String baseUsername = Username;			while (true) {				String filter = "(" + loginAttribute + "=" + Username + ")";				OIMLdapResultSet results = ldap.search(rootDN, filter);				if (results.getKeys().length == 0) {					result = Username;					break;				} else {					number++;					while (Integer.toString(number).length() + baseUsername.length() > MaxLength) {						baseUsername = baseUsername.substring(0, baseUsername.length() - 1);					}					Username = baseUsername + Integer.toString(number);				}			}		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::makeUsernameUnique()");		return result;	}		/**	 * Updates a single-valued ldap attribute for a user based on the ldap attribute -> form field lookup.	 * 	 * @param Username The username or other uniquely identifying attribute for the user to be updated.	 * @param AttributeLookup The name of the lookup table that maps the ldap attributes to form fields.	 * @param TaskKey The key of the process task instance that is calling this method.	 * @param ProcessInstanceKey The process instance key of the resource.	 * @return         <code>SUCCESS</code> if the method succeeds, or <code>FAILURE</code> if the operation fails.   	 *                 Details about any operational failure are logged to the system logger as error messages.	 */	public String setUserAttribute(String Username, String AttributeLookup, String TaskKey, long ProcessInstanceKey) {		String result = "FAILURE";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::setUserAttribute()");		logger.debug("Username = " + Username);		logger.debug("AttributeLookup = " + AttributeLookup);		logger.debug("TaskKey = " + TaskKey);		logger.debug("ProcessInstanceKey = " + Long.toString(ProcessInstanceKey));		try {			ldap.connect();			String field = getFormFieldForTask(TaskKey, ProcessInstanceKey);			String ldapAttribute = getAttributeForFormField(AttributeLookup, field);			String value = getProcessFormValue(ProcessInstanceKey, field);			result = setAttributeValue(Username, ldapAttribute, value);		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		} finally {			try {				if (ldap != null) {					ldap.disconnect();				}			} catch (Exception e) {				OIMExceptionHandler.handleException(logger, e, getClass());			}		}		logger.debug("result = " + result);		logger.debug("Exiting ::setUserAttribute()");		return result;	}		/**	 * Find the name of the form field handled by an update task.	 * 	 * @param TaskKey            The key of the process task instance that is calling this method.	 * @param ProcessInstanceKey The process instance key of the resource.	 * @return                   The name of the form field being updated, or an empty string if 	 *                           the field is not found in the mappings.	 */	public String getFormFieldForTask(String TaskKey, long ProcessInstanceKey) {		String result = "";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::getFormFieldForTask()");		logger.debug("TaskKey = " + TaskKey);		logger.debug("ProcessInstanceKey = " + Long.toString(ProcessInstanceKey));		try {			String taskName = (String) getOimData("select mil_name from osi inner join mil on osi.mil_key = mil.mil_key where sch_key = " + TaskKey)[0].get("Process Definition.Tasks.Task Name");			logger.debug("taskName = " + taskName);			String label = taskName.replaceAll(" Updated$", "");			logger.debug("label = " + label);			Map displayNames = getProcessFormDisplayNames(ProcessInstanceKey);			for (Iterator iterator = displayNames.keySet().iterator(); iterator.hasNext(); ) {				result = (String) iterator.next();				if (((String) displayNames.get(result)).equals(label)) {					break;				} else {					result = "";				}			}		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		}		logger.debug("result = " + result);		logger.debug("Exiting ::getFormFieldForTask()");		return result;	}		/**	 * Find the name of the ldap attribute for a task based on the ldap attribute -> form field lookup.	 * 	 * @param AttributeLookup The name of the lookup table that maps the ldap attributes to form fields.	 * @param FormField       The name of the form field being updated.	 * @return                The name of the ldap attribute to update, or an empty string if the attribute 	 *                        is not found in the mappings.	 */	public String getAttributeForFormField(String AttributeLookup, String FormField) {		String result = "";		if (!isInitialized) {			return result;		}		logger.debug("Entering ::getAttributeForFormField()");		logger.debug("AttributeLookup = " + AttributeLookup);		logger.debug("FormField = " + FormField);		try {			Map attributes = getLookupValues(AttributeLookup);			for (Iterator iterator = attributes.keySet().iterator(); iterator.hasNext(); ) {				result = (String) iterator.next();				if (((String) attributes.get(result)).equals(FormField)) {					break;				} else {					result = "";				}			}			if (result.length() == 0) {				logger.debug("Unabled to find ldap attribute for " + FormField);			}		} catch (Exception e) {			OIMExceptionHandler.handleException(logger, e, getClass());		}		logger.debug("result = " + result);		logger.debug("Exiting ::getAttributeForTask()");		return result;	}}